"""
命令行 MJ Web API 客户端

date: 2025/8/27
author: SiHeng Tang
file: cli-client.py
copyright(c) DFSA Eastern Software
此程序不能提供任何担保 WITHOUT WARRANTY OF ANY KIND
"""
import json
import logging
import queue
import sys

import client_core


def _parse_param(param_item: str):
    """
    解析参数前缀，转换参数类型
    :param param_item: 原始参数
    :return: 如果类型转换成立，则返回转换后的值，否则返回原值
    """
    try:
        if param_item.startswith(".float"):
            return float(param_item[6:])
        elif param_item.startswith(".int"):
            return int(param_item[4:])
        else:
            return param_item

    except ValueError:
        return param_item


class CliClient:
    """
    基础命令客户端，提供基于控制台的 MJ RPC 完全访问
    """
    CLI_RPC_MESSAGE = 0
    CLI_RPC_COMMAND = 1
    CLI_RPC_UPDATE = 2
    CLI_RPC_TOPIC = 3

    CONSOLE_END_L = '\n'
    default_port = "serial:/dev/ttyUSB0"

    def __init__(self, _ci: client_core.MJWebAPIClient | None = None):
        """
        初始化连接
        :param _ci: 如果传入 client_core 实例，就不做任何多余初始化，命令行客户端将完全使用这个实例，
        保持默认值，程序将询问连接地址并尝试连接
        """
        self.ci_core = client_core.MJWebAPIClient(True)

        if p := input(f"Remote path [{self.default_port}]: "):
            self.remote_path = p
        else:
            self.remote_path = self.default_port

        if self.ci_core.connect_remote(self.remote_path):
            sys.stdout.write(f"Connect to {self.remote_path} successfully!\n")
            self.ci_core.start()
        else:
            sys.stdout.write(f"Connect to {self.remote_path} failed!\n")
            exit(1)

        self._auto_recv_topics = []

    def _ask_what_request(self):
        """
        询问发送的数据包类型
        :return: 根据类静态变量映射的类型整数，方法字符串，参数对象
        """
        while True:
            print("\nSelect which kind of requests to send:")
            print(f"    [{self.CLI_RPC_MESSAGE}] MJ RPC message method call.")
            print(f"    [{self.CLI_RPC_COMMAND}] MJ RPC common method call.")
            print(f"    [{self.CLI_RPC_UPDATE}] Only update result.")
            print(f"    [{self.CLI_RPC_TOPIC}] MJ RPC auto topic subscribe (client implement).")
            print(f"    [Ctrl + C] To exit this console.")

            try:
                req_type = int(input(">"))
            except ValueError:
                continue

            if (req_type == self.CLI_RPC_COMMAND) or (req_type == self.CLI_RPC_MESSAGE):
                req_method = input("Request method: ")
                req_params = input("Request params: ")

                # 有一点小逻辑，为了支持其他类型参数和多参数列表，使用了比较独特的风格
                # 支持列表和 str int float 三种输入
                if " " in req_params:
                    str_list_para = req_params.split(" ")
                    req_params = []
                    for p in str_list_para:
                        req_params.append(_parse_param(p)) if bool(p) else None
                else:
                    req_params = _parse_param(req_params)

                return req_type, req_method, req_params

            # 提供一类专用的消息订阅，自动打印输出的消息
            elif req_type == self.CLI_RPC_TOPIC:
                req_method = input("Topic method: ")

                return req_type, req_method, "subscribe"

            else:
                return req_type, None, None

    def handle_batch(self):
        """
        处理一个批次的事件
        :return:
        """
        try:
            t, m, p = self._ask_what_request()

        except KeyboardInterrupt:
            sys.stdout.write("\nKeyboard Interrupt caused program exit. Bye!\n")
            exit(0)

        else:
            if t != self.CLI_RPC_UPDATE:
                msg_id = self.ci_core.commit_a_request(m, p, not bool(t))

                if t == self.CLI_RPC_TOPIC:
                    self._auto_recv_topics.append(msg_id)

        try:
            while True:
                res = self.ci_core.receive_a_result(blocking=True, timeout=0.1)

                if ("id" in res) and ("result" in res):
                    if res["id"] in self._auto_recv_topics:
                        if type(res["result"]) == list:
                            for i in res["result"]:
                                sys.stdout.write(str(i) + self.CONSOLE_END_L)
                        else:
                            sys.stdout.write(str(res["result"]) + self.CONSOLE_END_L)
                    else:
                        sys.stdout.write(json.dumps(res, indent=4) + self.CONSOLE_END_L)
                else:
                    sys.stdout.write(json.dumps(res, indent=4) + self.CONSOLE_END_L)

        except queue.Empty:
            pass

    def main_loop(self):
        while True:
            self.handle_batch()


if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO)

    c = CliClient()
    c.main_loop()
